#include<stdio.h>
#include<stdlib.h>
#define MAXN 1001  //从1开始
#define INF 100000
int Map[MAXN][MAXN],Cost[MAXN],N,M;
int parent[MAXN];  //用于并查集存放父亲下标
typedef struct Edge  //边节点
{
   int V;
   int W;
   int Cost;
}Edge;
Edge *E;
void CreateGraph(int n, int e)  //创建初始化图
{
   int v,w,i;
   for(v=1;v<=n;v++)
   {
       Cost[v]=0;    //存放各顶点到最小生成树的距离（价格）
       for(w=1;w<=n;w++) 
       {
           if(v==w)
              Map[v][w]=0;  //自己到自己不要钱
           else 
              Map[v][w]=INF;
       }
   }
   E=(Edge*)malloc(sizeof(struct Edge)*e);  //开一个边的动态数组
   for(i=0;i<e;i++)
   {
       scanf("%d %d %d",&E[i].V,&E[i].W,&E[i].Cost); //赋值边
       Map[E[i].V][E[i].W]=E[i].Cost;  //邻接矩阵权重赋值
       Map[E[i].W][E[i].V]=E[i].Cost;
   }
}
/*************************************Prime算法******************************************/
int FindMinCost()  //找最小价格
{
    int i,MinCost,MinCostPosition;
    MinCost=INF;
    MinCostPosition=0;
    for(i=1;i<=N;i++)
    {
       if(Cost[i] && Cost[i]<MinCost)  //如果价格不等于0，且更小
       {
           MinCost=Cost[i];
           MinCostPosition=i;
       }
    }
    return MinCostPosition;
}
int Prime()
{
    int i,GrossCost=0,MinCost,Count=0;
    for(i=1;i<=N;i++)Cost[i]=Map[1][i];   //从第1个村出发到各村的价格
    while(1)
    {
        MinCost=FindMinCost();
        if(MinCost==0)break;      //找不到最小的价格
        else  //找到最小价格，最小生成树长大
        {
            GrossCost+=Cost[MinCost];  //总价格更新
            Count++;  //计数器更新
            Cost[MinCost]=0;
        for(i=2;i<=N;i++)
        {
            if(Cost[i] && Cost[i]>Map[MinCost][i])  //如果价格不等于0，且各还没收入顶点到更新后的最小生成树距离更小
            {
               Cost[i]=Map[MinCost][i];  //那么更新距离
            }
        }
        }
    }
    if(Count<N-1)return -1;
    else return GrossCost;
}
/*************************************Kruskal算法******************************************/
void Shell_Sort()  //希尔排序
{
    int Si, D, P, i;
    Edge Tmp;
    int Sedgewick[] = {3905,2161,929, 505, 209, 109, 41, 19, 5, 1, 0};
    for ( Si=0; Sedgewick[Si]>=M; Si++ ) ; 
    for ( D=Sedgewick[Si]; D>0; D=Sedgewick[++Si] )
        for ( P=D; P<M; P++ )  //插入排序
        {
            Tmp = E[P];
            for ( i=P; i>=D && E[i-D].Cost >Tmp.Cost ; i-=D )
                E[i] = E[i-D] ;
            E[i] = Tmp;
        }
}
int FindParent(int v)  //查找集合
{
    if(parent[v]==v) return v;
    else return FindParent(parent[v]);
}
int Associate(int v,int w)  //合并集合
{
    int root1,root2;
    root1=FindParent(v);  //找到父亲
    root2=FindParent(w);  //找到父亲
    if(root1!=root2)  //如果父亲不一样
    {
       parent[root2]=root1;  //合并两个集合
       return 1;
    }
    return 0;
}
int Kruskal()
{
    int i,GrossCost=0,Count=0;
    Shell_Sort();  //希尔排序边集
    for(i=1;i<=N;i++)parent[i]=i;   //每个顶点一开始自己就是自己的父亲
    for(i=0;i<M;i++)
    {
       if(Associate(FindParent(E[i].V ),FindParent(E[i].W )))
       {
           Count++;  //边数+1
           GrossCost+=E[i].Cost;  //总价格更新
       }
       if(Count==N-1)break;  //有N-1条边了结束
 }
    if(Count<N-1) return -1;  //边数不够
    else return GrossCost;
}
int main()
{
    scanf("%d%d",&N,&M);
    CreateGraph(N,M);
    //printf("%d\n",Prime());
    printf("%d\n",Kruskal());
    return 0;
}